/* Bugzilla C# Proxy Library
   Copyright (C) 2006, Dansk BiblioteksCenter A/S
   Mads Bondo Dydensborg, <mbd@dbc.dk>
   
   This library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.
   
   This library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.
   
   You should have received a copy of the GNU Lesser General Public
   License along with this library; if not, write to the Free Software
   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
*/
/*! \file
  \brief Encapsulation of a bug in Bugzilla.

  Bugs are, of course, what Bugzilla tracks. In order to do anything with bugs,
  you need bug objects. 

  Instances of this class can be used to manipulate a Bug in a
  Bugzilla server.

  You cannot directly create a \b Bug object. You should get a \b Bug
  object from Server.GetBug or Server.GetBugs.

  In order to actually instantiate a Bug, a Server instance must be
  supplied.
*/

using System;
using BugzProxy.ProxyStructs;

namespace BugzProxy
{
    /// <summary>This class Encapsulates a bug in Bugzilla</summary>
    /// <remarks>
    /// <para>One should assume that all operations on a <b>Bug</b> will create network
    /// traffic, unless specifically indicated they will not.</para>
    /// <para>Properties reflect settings that will not cause network traffic, except
    /// for <see cref="Resolution"/>, while methods typically involves the server
    /// on the other end.</para>
    /// <para>Currently there are no public constructors. You should get a <b>Bug</b>
    /// object from <see cref="Server.GetBug"/> or <see cref="Server.GetBugs"/>.
    /// </para>
    /// </remarks>
    public class Bug
    {
        private Server server;
        private BugInfo bi; // Must always be valid

        /*! \name Constructors */

        //@{

        // This assumes the bug already exists on the server side.
        /// <summary>
        /// Initialize a new instance of the <see cref="Bug"/> class.
        /// </summary>
        /// <param name="server">A <see cref="Server"/> instance that is associated with this bug</param>
        /// <param name="bi">Information about the bug, as retreived from the server</param>
        internal Bug(Server server, BugInfo bi)
        {
            this.server = server;
            this.bi = bi;
        }
        //@}

        /*! \name General methods */
        //@{ 

        /// <summary>Update bug from server</summary>
        /// <remarks>Get any changes to the bug from the server. This updates information
        /// such as the time/date of the last change to the bug, etc.</remarks>
        public void Update()
        {
            int[] ids = new int[] { bi.id };
            BugIds param;
            param.ids = ids;
            bi = server.Proxy.GetBugs(param).bugs[0];
        }

        /*! \example AppendComment.cs
         * This is an example on how to use the Bugzproxy.Bug.AppendComment method */

        /// <summary>Append a comment to the bug.</summary>
        /// <param name="comment">The comment to append</param>
        /// <param name="isPrivate"><b>true</b> to make this comment visible to members
        /// of Bugzilla's <c>insidergroup</c> only, <b>false</b> (or <b>null</b>) to
        /// make it visible to all members.</param>
        /// <param name="worktime">The work time of this comment. Can be <b>null</b>
        /// or 0 for no work time. Ignored if You are not in the <c>timetrackinggroup</c>.
        /// </param>
        /// <remarks>
        /// <para><b>Note:</b> Works with Bugzilla trunk (3.1.2+) only.</para>
        /// <para>If <paramref name="isPrivate"/> is <b>null</b>, the comment is assumed
        /// public.</para>
        /// </remarks>
        public void AppendComment(string comment, bool? isPrivate, double? worktime)
        {
            AppendCommentParam param = new AppendCommentParam();
            param.id = bi.id;
            param.comment = comment;
            param.isPrivate = isPrivate;
            param.workTime = worktime;
            server.Proxy.AppendComment(param);
            /*! \todo, call update? */
        }

        /// <summary>
        /// Append a comment to the bug.
        /// </summary>
        /// <param name="comment">The comment to append</param>
        /// <remarks><b>Note:</b> Works with Bugzilla trunk (3.1.2+) only</remarks>
        public void AppendComment(string comment)
        {
            AppendComment(comment, null, null);
        }

        /// <summary>
        /// Append a comment to the bug.
        /// </summary>
        /// <param name="comment">The comment to append</param>
        /// <param name="isPrivate"><b>true</b> to make this comment visible to members
        /// of Bugzilla's <c>insidergroup</c> only, <b>false</b> (or <b>null</b>) to
        /// make it visible to all members.</param>
        /// <remarks><b>Note:</b> Works with Bugzilla trunk (3.1.2+) only.</remarks>
        public void AppendComment(string comment, bool? isPrivate)
        {
            AppendComment(comment, isPrivate, null);
        }

        /// <summary>
        /// Append a comment to the bug.
        /// </summary>
        /// <param name="comment">The comment to append</param>
        /// <param name="worktime">The work time of this comment. Can be <b>null</b>
        /// or 0 for no work time. Ignored if You are not in the <c>timetrackinggroup</c>.
        /// </param>
        /// <remarks><b>Note:</b> Works with Bugzilla trunk (3.1.2+) only</remarks>
        public void AppendComment(string comment, double? worktime)
        {
            AppendComment(comment, null, worktime);
        }

        //@}

        /*! \name Experimental 
             Experimental methods require patches. */
        //@{

        /// <summary>
        /// Get or set the bug resolution
        /// </summary>
        /// <value>A <b>string</b> with a legal resolution value for this bug.</value>
        /// <remarks>
        /// <para>The <b>set</b> part of this property is not implemented in Bugzilla,
        /// and requires an unpublished patch. It calls a web service named <c>Bug.set_resolution</c>
        /// (and therefore creates network traffic).</para>
        /// <para>The <b>get</b> part of this property is <b>UNSTABLE</b>, and may stop
        /// working even with minor (point) updates of Bugzilla.</para>
        /// <para>If the bug is not resolved yet, an empty string is returned.</para>
        /// </remarks>
        public string Resolution
        {
            get
            {
                return bi.internals["resolution"] as string;
            }
            set
            {
                SetBugResolutionParam parameters;
                parameters.bugId = bi.id;
                parameters.resolution = value;
                server.Proxy.SetBugResolution(parameters);
                Update();
            }
        }

        //@}

        /// <summary>
        /// Get the bug id number
        /// </summary>
        /// <value>The bug id.</value>
        public int Id
        {
            get
            {
                return bi.id;
            }
        }

        /// <summary>
        /// Get the time the bug was created
        /// </summary>
        /// <value>The bug creation time.</value>
        public DateTime Created
        {
            get
            {
                return bi.created;
            }
        }

        /// <summary>
        /// Get the time the bug was last changed.
        /// </summary>
        /// <value>The bug last change time.</value>
        /// <remarks>A change may be a change in any of the bug fields, or a change
        /// in the status of an attachment of the bug.</remarks>
        public DateTime Changed
        {
            get
            {
                return bi.changed;
            }
        }

        /// <summary>
        /// Get the bug alias
        /// </summary>
        /// <value>The bug alias. If the bug does not have an alias, an empty string
        /// is returned.</value>
        public string Alias
        {
            get
            {
                return bi.alias;
            }
        }

        /// <summary>
        /// Get the bug summary
        /// </summary>
        /// <value>The bug summary</value>
        public string Summary
        {
            get
            {
                return bi.summary;
            }
        }

        /*! \name Unstable
             These properties and methods may break even between point releases of
             Bugzilla.
     
             These properties, as well as the <b>get</b> part of the Resolution property,
             were tested with Bugzilla 3.0.2 and 3.1.2+.
         */
        //@{

        /// <summary>
        /// Get the name of the severity of the bug.
        /// </summary>
        public string Severity
        {
            get
            {
                return bi.internals["bug_severity"] as string;
            }
        }

        /// <summary>
        /// Get the bug status
        /// </summary>
        public string Status
        {
            get
            {
                return bi.internals["bug_status"] as string;
            }
        }

        /// <summary>
        /// Get the product this bug belongs to from the server.
        /// </summary>
        /// <returns>A <see cref="Product"/> object this bug belongs to, or <b>null</b>
        /// if no product was specified for this bug (shouldn't happen).</returns>
        /// <remarks>This method creates network trafic, by accessing the
        /// <see cref="Server.GetProduct"/> method.</remarks>
        /// <exception cref="ArgumentOutOfRangeException">The id of the product specified
        /// for this bug is invalid, or you cannot access this product. If this exception
        /// is raised, it may be an indication of a database inconsitency.</exception>
        public Product GetProduct()
        {
            object productIdObj = bi.internals["product_id"];
            if (productIdObj != null)
            {
                int productId = Convert.ToInt32(productIdObj);
                Product ret = server.GetProduct(productId);
                return ret;
            }
            return null;
        }

        /// <summary>
        /// Get the product version this bug was found on
        /// </summary>
        /// <value>The <c>version</c> field of a bug</value>
        /// <remarks>
        /// The type of the return value is not constant. If the version is a floating
        /// point number, such as 0.5, the value is returned as a
        /// <c>double</c>. If the version is an integer, it is returned as an <c>int</c>.
        /// If it is not a number, it is returned as a <c>string</c>.
        /// </remarks>
        public Object Version
        {
            get
            {
                //if the version is a number, it is returned as <double> from bugzilla.
                //if it's a word, it is returned as a <string>.
                //if it's an integral number, it is returned as <int>.
                return bi.internals["version"];
            }
        }

        /// <summary>
        /// Get a value indicating if this bug is confirmed.
        /// </summary>
        /// <value><b>true</b> if the bug is confiremd, <b>false</b> if it isn't confirmed.
        /// </value>
        public bool IsConfirmed
        {
            get
            {
                return bi.internals["everconfirmed"].Equals(1);
                //there is also "isunconfirmed"
            }
        }

        /// <summary>
        /// Get the target milestone for this bug
        /// </summary>
        /// <remarks>
        /// <para>The target milestone is the product version by which this bug should be
        /// fixed.</para>
        /// <para>The type of the return value is not constant. If the version is a floating
        /// point number, such as 0.5, the value is returned as a
        /// <c>double</c>. If the version is an integer, it is returned as an <c>int</c>.
        /// If it is not a number, it is returned as a <c>string</c>.</para>
        /// </remarks>
        public Object TargetMilestone
        {
            get
            {
                return bi.internals["target_milestone"];
            }
        }

        /// <summary>
        /// Get the priority of this bug
        /// </summary>
        public string Priority
        {
            get
            {
                return bi.internals["priority"] as string;
            }
        }

        /// <summary>
        /// Get the resolution of this bug, if exists.
        /// </summary>
        /// <value>The resolution of the bug, or an empty string if the bug is not resolved
        /// yet.</value>
        /// <remarks>
        /// <para>This property differs from <see cref="Resolution"/> by that this method
        /// uses already implemented, although unstable, API, while <b>Resolution</b>
        /// requires implementation. However both should return the same information.
        /// </para>
        /// </remarks>
        //public string Resolution2 {
        //  get {
        //    return bi.internals["resolution"] as string;
        //  }
        //}

        /// <summary>
        /// Get the operating system this bug was reported on.
        /// </summary>
        public string OperatingSystem
        {
            get
            {
                return bi.internals["op_sys"] as string;
            }
        }

        /// <summary>
        /// Get the content of the bug Whiteboard field.
        /// </summary>
        public string StatusWhiteboard
        {
            get
            {
                return bi.internals["status_whiteboard"] as string;
            }
        }

        /* fields who's meaning is unknown, or that cannot be implemented.
        ["isopened"]: 1
        ["component_id"]: 2
        ["isunconfirmed"]: ""
        ["assigned_to_id"]: 1
        ["delta_ts"]: "2007-12-17 21:38:55"
        ["qa_contact_id"]: ""
        ["product_id"]: 2
        ["reporter_id"]: 1
        ["cclist_accessible"]: 1
        ["reporter_accessible"]: 1
        */
        //@}

    } // class Bug

} // namespace BugzProxy
